Что такое миграции и зачем они нужны?

Если Вы уже знаете, что такое миграции и зачем они нужны, но не знаете как с ними работать, то переходите к инструкции. Если у Вас еще нет представления о миграциях, то перед инструкцией почитайте пару абзацев.

Давайте представим, что над проектом работает 3 программиста. Каждый из них делает свою часть работы в процессе меняя и дописывая код, выгружает его в систему контроля версий. В процессе изменения исходного кода приложения меняется и структура базы данных. Добавляются новые таблицы и поля, лишнее удаляется и т.д.. Для того что бы отследить изменения в структуре БД, программисты договорились складывать все SQL запросы изменения БД в файл, имеющий имя текущей даты, в одну папку. Теперь у всех при пуле есть последняя версия исходного кода и список файлов с изменениями структуры БД. Перед продолжением работы, что бы не было ошибок, программисту необходимо выполнить все запросы из новых файлов и только после этого продолжить работу. Тим лид (он же главный) при деплое проекта не только будет копировать файлы на боевой сервер, но и импортировать все запросы по изменению структуры БД. Не слишком удобно, правда? Таких подходов с отслеживанием структуры БД есть много вариаций, я описал один из возможных.

Отслеживание изменения структуры базы данных и есть миграции. Каждая миграция - это новая версия БД. Т.е. в нашем случае миграция - обновление структуры базы данных от одной версии к другой.

В Yii реализована поддержка миграций, позволяющая применять изменения в структуре и отменять их.

Управление миграциями в Yii осуществляется с помощью консольной команды yiic migrate. Она отвечает за создание, применение, откат, повторное применение и просмотр истории миграций.

Для начала необходимо проверить настройки соединения с базой данных в файле protected/config/console.php.

<?php

    //...

    'db' => array(
        'connectionString' => 'mysql:host=localhost;dbname=main',
        'emulatePrepare' => true,
        'username' => 'root',
        'password' => '',
        'charset' => 'utf8',
        'enableProfiling' => true,
        ),

    //...

Проверьте существование директории protected/migrations и доступна ли она для записи.

Открываем консоль (для Windows Win + R и введите cmd).

Далее переходим в папку проекта (к примеру: D:/web/home/site.local/www) в директорию protected:


d:
cd /web/home/site.local/www/protected

Создаем миграцию


yiic migrate create [migrate_name]

[migrate_name] - это обязательный параметр, который должен содержать краткое описание миграции (например, create_comments_table). Этот параметр будет использоваться как часть имени класса миграции (в имени можно использовать буквы, цифры и нижнее подчеркивание).

Пример создания миграции, для новой таблицы комментариев:


yiic migrate create create_comments_table

При выполнении этой команды будет создан файл: m150108_091710_create_comments_table.php в папке protected/migrations, который будет содержать следующий код:

<?php

class m150108_091710_create_comments_table extends CDbMigration
{
    public function up()
    {
    }
 
 
    public function down()
    {
        echo "m150108_091710_create_comments_table does not support migration down.\n";
        return false;
    }
 
    /*
    // Используйте safeUp/safeDown для использования транзакций
    public function safeUp()
    {
    }
 
    public function safeDown()
    {
    }
    */
}

Имя файла и класса строится по шаблону: m[timestamp]_[migrate_name], где [timestamp] - это время создания миграции в формате yymmdd_hhmmss, а [migrate_name] - это имя указанное в параметре команды.

Рассмотрим методы класса подробнее:

Метод up() должен содержать код, который выполняет миграцию (добавление таблиц, полей, переименование таблиц, полей и т.д.).

Метод down() может содержать код, который будет выполнять отмену команд, выполненную в методе up() (откат структуры БД к предыдущей версии). Случается так, что реализовать откат в методе down() нельзя. Например при удалении данных из таблицы, вернуть их нет возможности. В таких случаях миграция считается необратимой, т.е. невозможно вернуть БД к предыдущему состоянию. В генерируемом Yii примере, метод down() возвращает false, т.е. откат миграции невозможен.

Для примера рассмотрим создание таблицы комментариев.

<?php

class m150108_091710_create_comments_table extends CDbMigration {
    public function up() {
        $this->createTable('comments', array(
            'id' => 'pk',
            'name' => 'string NOT NULL',
            'text' => 'text',
        ));
    }
 
    public function down() {
        $this->dropTable('comments');
    }
}

Список команд для работы с миграциями можете посмотреть здесь:
http://www.yiiframework.com/doc/api/1.1/CDbMigration

Если у Вас возникают сложности при написание запросов с помощью: createTable, addColumn, dropColumn, dropTable, renameColumn, renameTable и т.д.. Можете использовать прямые SQL запросы Yii::app()->db->createCommand. Пример:

<?php

class m150108_091710_create_comments_table extends CDbMigration {
    public function up() {
        $sql = "CREATE TABLE IF NOT EXISTS `comments` (
            `id` int(11) NOT NULL AUTO_INCREMENT,
            `name` varchar(255) NOT NULL,
            `text` TEXT,
            PRIMARY KEY (`id`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;";
        Yii::app()->db->createCommand($sql)->execute();
    }
 
    public function down() {
        $sql = "DROP TABLE comments";
        Yii::app()->db->createCommand($sql)->execute();
    }
}

Применение миграций


yiic migrate

Данная команда выводит список всех новых миграций и вопрос применить ли миграции:


Yii Migration Tool v1.0 (based on Yii v1.1.15)

Creating migration history table "comments" ...done/
Total 1 new migrations to be applied:
    m150108_091710_create_news_table

Apply the above migrations& (yes:no) [no]:

При положительном ответе, команда запустит по очереди метод up() в каждом из классов миграции в порядке их создания (в данном случае это 1 миграция).

После выполнения команды миграции будет создана таблица tbl_migration, если она еще не создана (таблица будет находиться в основной БД указанной в конфигурации db). В эту таблицу будет внесена запись, которая позволяет узнать, какие миграции уже применены, а какие нет.

Бывают ситуации в которых необходимо применить одну или несколько новых миграций. Для этого нужно использовать команду:


yiic migrate up [count]

Эта команда выполнит [count] новых миграции. [count] - любое количество применяемых миграций.

Если Вам необходимо привести БД в определенное состояние (версию), то можно использовать следующую команду:


yiic migrate to [version]

[version] - версия к которой необходимо привести базу данных, здесь используется часть имени файла, которая соответствует времени создания миграции (например: 101129_185401). Если указанная миграция уже применена, то будет произведён откат до этой миграции.

Транзакции в миграции

Для сложных миграций с использованием транзакций можно использовать встроенный механизм транзакции явно:

<?php

class m150108_091710_create_comments_table extends CDbMigration
{
    public function up()
    {
        $transaction=$this->getDbConnection()->beginTransaction();
        try
        {
            $this->createTable('comments', array(
                'id' => 'pk',
                'name' => 'string NOT NULL',
                'text' => 'text',
            ));
            $transaction->commit();
        }
        catch(Exception $e)
        {
            echo "Exception: ".$e->getMessage()."\n";
            $transaction->rollback();
            return false;
        }
    }
 
    //...
}

Или использовать механизм встроенный в миграции. Для этого вместо метода up() нужно использовать safeUp() и вместо метода down() используйте safeDown():

<?php

class m150108_091710_create_comments_table extends CDbMigration {
    public function safeUp() {
        $this->createTable('comments', array(
            'id' => 'pk',
            'name' => 'string NOT NULL',
            'text' => 'text',
        ));
    }
 
    public function safeDown() {
        $this->dropTable('comments');
    }
}

При выполнении миграции, Yii начнёт транзакцию и выполнит метод safeUp() или метод safeDown(). Если произойдет ошибка, сработает откат транзакции и БД вернется к состоянию до начала миграции.

Отмена миграций

Если Вам необходимо отменить миграцию или несколько последних миграций, то можно воспользоваться следующей командой:


yiic migrate down [step]

[step] - необязательный параметр, который задает количество отменяемых миграций. Если параметр не указан, то отменяется одна последняя миграция.

Если миграцию невозможно откатить, то будет отдано исключение и откат будет прерван.

Повторное применение миграций

Если во время выполнения миграции возникли какие-то проблемы, можно повторно применить миграцию. Это делается с помощью следующей команды:


yiic migrate redo [step]

[step] - необязательный параметр, который задает количество миграций, которые необходимо применить еще раз. Если параметр не указан, то повторно применяется одна последняя миграция.

Просмотр информации о миграциях

Для просмотра истории миграций которые уже применены или еще не применены, есть следующие команды:


yiic migrate history [limit]
yiic migrate new [limit]

[limit] - количество отображаемых миграций. Если этот параметр не указан, то будут показаны все миграции.

history - покажет примененные миграции.

new — список новых миграций, которые еще не применены.

Изменение истории миграций

Если Вам требуется применить миграцию без откатов или применений других миграций, то можно воспользоваться следующей командой:


yiic migrate mark [version]

Команда mark похожа на to, с тем отличием, что mark изменит таблицу tbl_migration до указанной версии без применения или отката миграций.

Все, теперь Вы можете попрактиковаться в применении миграций в Yii Framework и существенно облегчить отслеживание изменений структуры базы данных при работе в команде. Удачи!